using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Windows.Forms;
using System.Media;
using System.Linq;
using System.Xml;


namespace ClownStopper
{

    public partial class frmMain : Form
    {
        private string workGroup = "AssClown11";

        private int mListenPort;
        private string mServer = System.Configuration.ConfigurationManager.AppSettings["Server"];
        private int serverListenPort = int.Parse(System.Configuration.ConfigurationManager.AppSettings["ServerListenPort"]);

        private MessageServer _repositoryServer;
        private MessageClient _client;


        public frmMain(bool isServer)
        {
            InitializeComponent();

            _moorseCodeList = LoadMorseCodes();

            lstErrors.Items.Clear();
            tabPage3.Visible = false;
            #region Server
            if (isServer)
            {
                mListenPort = GetAvailablePort(1000);
                _repositoryServer = new MessageServer(serverListenPort, workGroup);
                _repositoryServer.Initialize();
                _repositoryServer.OnServerReceiveError += OnError;
                _repositoryServer.OnReceiveMessage += OnReceiveServerMessage;
                _client = new MessageClient(mListenPort, mServer, serverListenPort, workGroup);
                Text = "Clown Stopper - Server: Port " + mListenPort;
                tabPage3.Visible = true;

            }
            else
            {
                mListenPort = GetAvailablePort(2000);
                _client = new MessageClient(mListenPort, mServer, serverListenPort, workGroup);

                Text = "Clown Stopper - Client: Port " + mListenPort;
            }
            _client.Initialize();
            _client.SetupResponseListener();
            _client.OnClientReceiveError += OnError;
            _client.OnClientReceiveMessage += OnClientReceiveMessage;

            #endregion


            LoadBingoTable();
        }


        public static int GetAvailablePort(int startingPort)
        {
            IPEndPoint[] endPoints;
            List<int> portArray = new List<int>();

            IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();

            //getting active connections
            TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();
            portArray.AddRange(from n in connections
                               where n.LocalEndPoint.Port >= startingPort
                               select n.LocalEndPoint.Port);

            //getting active tcp listners - WCF service listening in tcp
            endPoints = properties.GetActiveTcpListeners();
            portArray.AddRange(from n in endPoints
                               where n.Port >= startingPort
                               select n.Port);

            //getting active udp listeners
            endPoints = properties.GetActiveUdpListeners();
            portArray.AddRange(from n in endPoints
                               where n.Port >= startingPort
                               select n.Port);

            portArray.Sort();

            for (int i = startingPort; i < UInt16.MaxValue; i++)
                if (!portArray.Contains(i))
                    return i;

            return 0;
        }

        #region ClownStopper Attack

        private const int SLEEP_WAIT_CLOWN = 700;
        private const int SLEEP_WAIT_AHOOGA = 1000;
        private const int SLEEP_WAIT_FOG = 2500;
        private const int SLEEP_WAIT_ARIAL = 3000;
        private const int SLEEP_WAIT_SLAP = 1000;
        private const int SLEEP_WAIT_PUNCH = 1000;
        private const int SLEEP_WAIT_GUN = 1000;
        private bool _stopbackup;
        private static bool _disableAutoResponse;
        private static Dictionary<string, List<int>> _moorseCodeList;
        private Dictionary<string, SoundPlayer> _sounds = new Dictionary<string, SoundPlayer>();

        void OnClientReceiveMessage(object sender, ReceiveMessageEventArgs e)
        {
            var msg = e.Message;
            switch (msg.Type)
            {
                case ((int)MessageType.TextDataMessage):
                    var rxMsg = (TextMessage)msg;
                    if (!_disableAutoResponse)
                    {
                        if (rxMsg.Text.IndexOf("Clown", StringComparison.InvariantCultureIgnoreCase) > -1)
                        {
                            btnClown_Click(sender, e);
                        }
                        if (rxMsg.Text.IndexOf("Ahooga", StringComparison.InvariantCultureIgnoreCase) > -1)
                        {
                            btnAhooga_Click(sender, e);
                        }
                        if (rxMsg.Text.IndexOf("Arial", StringComparison.InvariantCultureIgnoreCase) > -1)
                        {
                            btnArial_Click(sender, e);
                        }
                    }
                    Extensions.SetText(txtBackupMessage, rxMsg.Text);
                    break;
                case ((int)MessageType.BingoMessage):
                    try
                    {
                        var rxBingoMsg = (BingoMessage)msg;
                        Extensions.SetText(txtBackupMessage, rxBingoMsg.Text);
                        MessageBox.Show(string.Format("{0} from {1}", rxBingoMsg.Text, rxBingoMsg.ClientIp), "BINGO", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                    catch
                    {
                    }
                    break;

            }


        }


        void OnError(object sender, ErrorEventArgs e)
        {
            lstErrors.Items.Add(e.Message);
        }

        private void OnReceiveServerMessage(object sender, ReceiveServerMessageEventArgs e)
        {
            var msg = e.Message;


            switch (msg.Type)
            {
                case ((int)MessageType.TextDataMessage):
                    {
                        var rxMsg = (TextMessage)msg;
                        Extensions.SetText(txtBackupMessage, rxMsg.Text);
                        //sedn to the client
                        foreach (var messageClientDetails in _repositoryServer.GroupClientsDetails)
                        {
                            //dont send to yourself
                            if (!messageClientDetails.ClientIPAddress.Equals(e.ClientIp))
                            {
                                _repositoryServer.SendMessage(messageClientDetails.ClientIPAddress, messageClientDetails.ClientListenPort, msg);
                            }
                        }
                    }
                    break;
                case ((int)MessageType.BingoMessage):
                    {
                        var rxBingoMsg = (BingoMessage)msg;
                        Extensions.SetText(txtBackupMessage, rxBingoMsg.Text);
                        //sedn to the client
                        foreach (var messageClientDetails in _repositoryServer.GroupClientsDetails)
                        {
                            _repositoryServer.SendMessage(messageClientDetails.ClientIPAddress, messageClientDetails.ClientListenPort, msg);
                        }
                    }
                    break;
                default:
                    lstErrors.Items.Add("Un-supportesd message type" + msg.Type);                   
                    break;
            }

        }

        private void PlaySound(UnmanagedMemoryStream resourceToPlay)
        {
            try
            {

                var sndplayr = new SoundPlayer(resourceToPlay);
                sndplayr.Play();

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ": " + ex.StackTrace, "Error");
            }
        }

        private void btnAhooga_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.ahooga);
            Thread.Sleep(SLEEP_WAIT_AHOOGA);
        }

        private void btnFog_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.foghorn);
            Thread.Sleep(SLEEP_WAIT_FOG);
        }

        private void btnClown_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.clown);
            Thread.Sleep(SLEEP_WAIT_CLOWN);
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            Left = (Screen.PrimaryScreen.WorkingArea.Width / 2);
            Top = (Screen.PrimaryScreen.WorkingArea.Height / 2);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void btnPlay_Click(object sender, EventArgs e)
        {
            btnPlay.Text = @"Firing";
            btnPlay.Enabled = false;
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            foreach (object item in chkWeapons.CheckedItems)
            {
                if (item.ToString().Equals("Clown", StringComparison.InvariantCultureIgnoreCase))
                {
                    btnClown_Click(sender, e);
                }
                if (item.ToString().Equals("Ahooga", StringComparison.InvariantCultureIgnoreCase))
                {
                    btnAhooga_Click(sender, e);
                }
                if (item.ToString().Equals("Fog", StringComparison.InvariantCultureIgnoreCase))
                {
                    btnFog_Click(sender, e);
                }
                if (item.ToString().Equals("Fly By", StringComparison.InvariantCultureIgnoreCase))
                {
                    btnArial_Click(sender, e);
                }
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            btnPlay.Enabled = true;
            btnPlay.Text = @"Fire";
            timer1.Stop();
        }

        private void btnTransmitMessage_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtSos.Text))
            {
                MessageBox.Show("You must provide an SOS message");
                return;
            }
            btnCancelTransmission.Enabled = true;
            var letters = txtSos.Text.ToUpperInvariant().ToCharArray();
            foreach (var letter in letters)
            {
                Application.DoEvents();
                if (_stopbackup)
                {
                    break;
                }
                Application.DoEvents();
                var notes = GetPlayList(letter.ToString());
                foreach (var note in notes)
                {
                    switch (note)
                    {
                        case 0:
                            btnClown_Click(sender, e);
                            break;
                        case 1:
                            btnAhooga_Click(sender, e);
                            break;
                        case 3:
                            btnFog_Click(sender, e);
                            break;
                    }
                }
            }
            _stopbackup = false;
            btnCancelTransmission.Enabled = false;
        }

        private List<int> GetPlayList(string letter)
        {
            try
            {
                return _moorseCodeList[letter];
            }
            catch (Exception)
            {

                return new List<int> { 3 };
            }

        }

        private Dictionary<string, List<int>> LoadMorseCodes()
        {
            var codes = new Dictionary<string, List<int>>();

            codes.Add("A", new List<int> { 0, 1 });
            codes.Add("B", new List<int> { 1, 0, 0, 0 });
            codes.Add("C", new List<int> { 1, 0, 1, 0 });
            codes.Add("D", new List<int> { 1, 0, 0 });
            codes.Add("E", new List<int> { 0 });
            codes.Add("F", new List<int> { 0, 0, 1, 0 });
            codes.Add("G", new List<int> { 1, 1, 0 });
            codes.Add("H", new List<int> { 0, 0, 0, 0 });
            codes.Add("I", new List<int> { 0, 0 });
            codes.Add("J", new List<int> { 0, 1, 1, 1 });
            codes.Add("K", new List<int> { 1, 0, 1 });
            codes.Add("L", new List<int> { 0, 1, 0, 0 });
            codes.Add("M", new List<int> { 1, 1 });
            codes.Add("N", new List<int> { 1, 0 });
            codes.Add("O", new List<int> { 1, 1, 1 });
            codes.Add("P", new List<int> { 0, 1, 1, 0 });
            codes.Add("Q", new List<int> { 1, 1, 0, 1 });
            codes.Add("R", new List<int> { 0, 1, 0 });
            codes.Add("S", new List<int> { 0, 0, 0 });
            codes.Add("T", new List<int> { 1 });
            codes.Add("U", new List<int> { 0, 0, 1 });
            codes.Add("V", new List<int> { 0, 0, 0, 1 });
            codes.Add("W", new List<int> { 0, 1, 1 });
            codes.Add("X", new List<int> { 1, 0, 0, 1 });
            codes.Add("Y", new List<int> { 1, 0, 1, 1 });
            codes.Add("Z", new List<int> { 1, 1, 0, 0 });

            codes.Add("1", new List<int> { 0, 1, 1, 1, 1 });
            codes.Add("2", new List<int> { 0, 0, 1, 1, 1 });
            codes.Add("3", new List<int> { 0, 0, 0, 1, 1 });
            codes.Add("4", new List<int> { 0, 0, 0, 0, 1 });
            codes.Add("5", new List<int> { 0, 0, 0, 0, 0 });
            codes.Add("6", new List<int> { 1, 0, 0, 0, 0 });
            codes.Add("7", new List<int> { 1, 1, 0, 0, 0 });
            codes.Add("8", new List<int> { 1, 1, 1, 0, 0 });
            codes.Add("9", new List<int> { 1, 1, 1, 1, 0 });
            codes.Add("0", new List<int> { 1, 1, 1, 1, 1 });

            codes.Add(".", new List<int> { 0, 1, 0, 1, 0, 1 });
            codes.Add(",", new List<int> { 1, 1, 0, 0, 1, 1 });

            return codes;

        }

        private void btnCancelTransmission_Click(object sender, EventArgs e)
        {
            _stopbackup = true;
        }

        private void btnArial_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.geeseflyhonk);
            Thread.Sleep(SLEEP_WAIT_ARIAL);
        }

        private void btnSlap_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.slap);
            Thread.Sleep(SLEEP_WAIT_SLAP);
        }

        private void btnPunch_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.punch);
            Thread.Sleep(SLEEP_WAIT_PUNCH);
        }

        private void btnGun_Click(object sender, EventArgs e)
        {
            PlaySound(Properties.Resources.Gun);
            Thread.Sleep(SLEEP_WAIT_GUN);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var selectedItem = cmbBackupMessage.Text;
            if (!string.IsNullOrEmpty(selectedItem))
            {
                var sendText = selectedItem;
                if (selectedItem.IndexOf("Send In The Clowns", StringComparison.InvariantCultureIgnoreCase) > -1)
                {
                    sendText = "Clown";
                }
                if (selectedItem.IndexOf("Can I Get An Ahooga", StringComparison.InvariantCultureIgnoreCase) > -1)
                {
                    sendText = "Ahooga";
                }
                if (selectedItem.IndexOf("Hit Em From Above", StringComparison.InvariantCultureIgnoreCase) > -1)
                {
                    sendText = "Arial";
                }
                var txt = new TextMessage { Text = sendText, ClientIp = _client.LocalIp.ToString() };
                if (_client != null)
                {
                    _client.SendMessage(txt);
                }
                else
                {
                    MessageBox.Show("_client is null");
                }
            }
            else
            {
                MessageBox.Show("You must provide details of the attack");
            }

        }

        private void lnkClear_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            txtSos.Text = string.Empty;
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            _disableAutoResponse = checkBox1.Checked;
        }
        #endregion

        private void lnkClearLog_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            lstErrors.Items.Clear();
        }


        #region Bingo

        private XmlNodeList _loadedTerms;
        private XmlNodeList BingoTerms
        {
            get
            {
                if (_loadedTerms == null)
                {
                    var doc = new XmlDocument();
                    doc.LoadXml(Properties.Resources.Bingo);
                    _loadedTerms = doc.GetElementsByTagName("Item");
                }
                return _loadedTerms;
            }
        }

        private void LoadBingoTable()
        {
            var colCount = 0;
            var rowCount = 0;
            var termCount = BingoTerms.Count;
            var pickedList = new List<string>();
            var random = new Random();
            for (int i = 0; i < 25; i++)
            {
                if (colCount > 5)
                {
                    colCount = 0;
                    rowCount += 1;
                }
                int index = random.Next(termCount);
                var label = GetValidElement(random, termCount, pickedList);
                if (!pickedList.Contains(label))
                {
                    tblBingo.Controls.Add(CreateBingoPanel(label), colCount, rowCount);
                    pickedList.Add(label);
                }

                colCount += 1;

            }
        }

        private string GetValidElement(Random random, int termCount, List<string> pickedList)
        {
            while (true)
            {
                int index = random.Next(termCount);
                var label = BingoTerms[index].InnerText;
                if (!pickedList.Contains(label))
                {
                    return label;
                }
            }

        }

        private TableLayoutPanel CreateBingoPanel(string lbl)
        {
            var tbl = new TableLayoutPanel();
            tbl.ColumnCount = 1;
            tbl.RowCount = 2;
            tbl.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
            tbl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
            // Padding (pixels)within each cell (left, top, right, bottom)
            tbl.Padding = new Padding(1, 1, 4, 5);
            var check = new CheckBox();
            var label = new Label();
            check.CheckedChanged += check_CheckedChanged;
            check.Tag = lbl;
            check.TextAlign = ContentAlignment.MiddleCenter;
            check.Bounds = new Rectangle(25, 20, 20, 20);

            check.Anchor = AnchorStyles.None;
            label.TextAlign = ContentAlignment.MiddleCenter;
            label.Text = lbl;
            label.Dock = DockStyle.Fill;
            label.Font = new Font("Arial", 9, FontStyle.Bold);
            label.ForeColor = Color.Black;
            tbl.Dock = DockStyle.Fill;
            tbl.Controls.Add(label);
            tbl.Controls.Add(check);

            return tbl;
        }

        void check_CheckedChanged(object sender, EventArgs e)
        {
            var chk = (CheckBox)sender;
            if (chk.Checked)
            {
                var bingo = HasBingo();
                if (bingo)
                {
                    //send the message out
                    var txt = new BingoMessage() { Text = "Bingo!!", ClientIp = _client.LocalIp.ToString() };
                    if (_client != null)
                    {
                        _client.SendMessage(txt);
                    }
                    else
                    {
                        MessageBox.Show("_client is null");
                    }
                }

            }
        }

        private bool HasBingo()
        {
            //check rows
            var bingoCountRow = 0;
            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    var innerTbl = (TableLayoutPanel)tblBingo.GetControlFromPosition(j, i);
                    var chk = (CheckBox)innerTbl.GetControlFromPosition(0, 1);
                    if (chk.Checked)
                    {
                        bingoCountRow += 1;
                    }
                }
                if (bingoCountRow == 5)
                {
                    //var bingo
                    return true;
                }
            }

            //check columns
            var bingoCountCol = 0;
            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    var innerTbl = (TableLayoutPanel)tblBingo.GetControlFromPosition(i, j);
                    var chk = (CheckBox)innerTbl.GetControlFromPosition(0, 1);
                    if (chk.Checked)
                    {
                        bingoCountCol += 1;
                    }
                }
                if (bingoCountCol == 5)
                {
                    //var bingo
                    return true;
                }
            }

            //check diag
            var bingoCountDiag = 0;
            for (int i = 0; i < 5; i++)
            {
                var innerTbl = (TableLayoutPanel)tblBingo.GetControlFromPosition(i, i);
                var chk = (CheckBox)innerTbl.GetControlFromPosition(0, 1);
                if (chk.Checked)
                {
                    bingoCountDiag += 1;
                }
            }
            if (bingoCountDiag == 5)
            {
                //var bingo
                return true;
            }


            return false;
        }

        #endregion

        private void newBingoCardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tblBingo.Controls.Clear();
            LoadBingoTable();
        }


    }
}